package org.snia.exception;

import java.io.PrintWriter;
import java.io.StringWriter;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class ExceptionUtil
{

    /**
     * 异常栈字符串转换方法
     * 
     * @param throwable 待处理异常对象
     * @return String 异常栈字符串
     */
    public static String getStackTrace(Throwable throwable)
    {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw, true);
        throwable
            .printStackTrace(pw);
        return sw
            .getBuffer().toString();
    }

    /**
     * 异常类型判断
     * 
     * @param e 待处理异常对象
     * @param clazz 用于比较的异常类的类模板
     * @return true -- 该异常属于指定的异常对象 false -- 该异常不属于指定的异常对象
     */
    public static boolean isException(Exception e, Class<?> clazz)
    {
        return e
            .getClass().getName().equals(clazz
                .getName());
    }

    /**
     * 判断一个异常是否是BusinessException
     * 
     * @param e 待处理异常对象
     * @return true -- 该异常属于BusinessException false -- 该异常不属于BusinessException
     */
    public static boolean isBusinessException(Exception e)
    {
        return isException(e, BusinessException.class);
    }

    /**
     * 异常处理公用方法 为了不让上层异常覆盖下层异常，并能捕获所有异常作为统一异常抛出, 对于传入异常进行匹配，符合类型的直接抛出，其他的进行转换处理.
     * 为了便于记录异常信息，调用者的类需要传入. 通过该方法处理异常，最后所得到的异常消息来自于最先抛出的部分，而不会因为
     * 调用关系以及代码分层而被覆盖。在这种情况下，每一个方法可以提供一个唯一的
     * 异常消息来表示该方法，代码会根据情况确定是否将该消息作为为真正的错误消息, 这一特性对于需要向一个调用者反馈准确的错误消息很重要
     * 
     * @param e 待处理异常
     * @param clazz 发生异常的业务类
     * @param erroMsg 针对于调用方法的异常的错误消息
     * @return 下层的BusinessException或者本方法的Exception转换得到的BusinessException
     */
    public static BusinessException transToBusinessException(Exception e,
        Class<?> clazz, String erroMsg)
    {
        if (isBusinessException(e))
        {
            // 目标类型异常直接抛出,这样避免覆盖
            return (BusinessException) e;
        }
		// 其他异常记录异常栈后转换抛出
		return new BusinessException(erroMsg, e);
    }

    /**
     * 异常处理公用方法 为了不让上层异常覆盖下层异常，并能捕获所有异常作为统一异常抛出, 对于传入异常进行匹配，符合类型的直接抛出，其他的进行转换处理.
     * 为了便于记录异常信息，调用者的类需要传入. 通过该方法处理异常，最后所得到的异常消息来自于最先抛出的部分，而不会因为
     * 调用关系以及代码分层而被覆盖。在这种情况下，每一个方法可以提供一个唯一的
     * 异常消息来表示该方法的异常，代码会根据情况确定是否将该消息作为为真正的错误消息, 这一特性对于需要向一个调用者反馈准确的错误消息很重要
     * 
     * @param e 待处理异常
     * @param clazz 发生异常的业务类
     * @param errorMsg 针对于调用方法的异常的错误消息
     * @param errorCode 针对于调用方法的异常的错误码
     * @return BusinessException
     *         下层的BusinessException或者本方法的Exception转换得到的BusinessException
     */
    public static BusinessException transToBusinessException(Exception e,
        Class<?> clazz, String errorMsg, String errorCode)
    {
        if (isBusinessException(e))
        {
            // 目标类型异常直接抛出,这样避免覆盖
            return (BusinessException) e;
        }
		// 其他异常记录异常栈后转换抛出;
		Log log = LogFactory.getLog(clazz);
		log
		    .error("erroMsg:" + e
		        .getMessage());
		log
		    .error(getStackTrace(e));
		return new BusinessException(errorMsg, errorCode, e);
    }
}
